#include <windows.h>
#include <glf/config.h>

#include <glf/net/socket.h>

#include <Iphlpapi.h>
#pragma comment(lib, "IPHLPAPI.lib")

#ifndef GLF_WINDOWS_DISABLE_SDL
#	include <sdl.h>
#	include <SDL_syswm.h>
#endif

#if GLF_ENABLE_DEBUGGER
#	include <glf/debugger/debugger.h>
#endif

#	include <glf/remote/controller.h>

namespace glf {

struct DeviceInfos {
	const char* name;
	size_t screenWidth;
	size_t screenHeight;
} gDeviceInfos[] = {
	{ "default",	640,	480 },
	{ "iPodTouch",	480,	320 },
	{ "iPodTouch4",	960,	640 },
	{ "iPhone3g",	480,	320 },
	{ "iPhone3gs",	480,	320 },
	{ "iPhone4",	960,	640 },
	{ "iPhone4s",	960,	640 },
	{ "iPad",		1024,	768 },
	{ "iPad3",		2048,	1536 },
	{ "iPad4",		2048,	1536 },
};

const DeviceInfos& GetDeviceInfo(const char* name) {

	if(name) {
		for(size_t i = 0; i < GLF_ASIZEOF(gDeviceInfos); ++i) {
			const DeviceInfos& di = gDeviceInfos[i];
			if(Stricmp(name, di.name) == 0) {
				return di;
			}
		}
	}

	return gDeviceInfos[0];
}

char gDeviceName[32] = { 0 }; 
int gArgPosX = CreationSettings::DEFAULT_VALUE;
int gArgPosY = CreationSettings::DEFAULT_VALUE;
char gRemoteDeviceIP[16] = {0};

void EmptyDirectory(char* folderPath) {
	char buffer[MAX_PATH];
	JoinPath(buffer, folderPath, "*");
	
	WIN32_FIND_DATA info;
	HANDLE hp = FindFirstFile(buffer, &info);
	
	if(hp != INVALID_HANDLE_VALUE) {
		do {
			const char* filename = info.cFileName;

			if(strcmp(filename, ".") == 0 || strcmp(filename, "..") == 0) {
				continue;
			}

			if((info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)== FILE_ATTRIBUTE_DIRECTORY) {
				JoinPath(buffer, folderPath, filename);
				EmptyDirectory(buffer);
				RemoveDirectory(buffer);
			} else {
				JoinPath(buffer, folderPath, filename);
				DeleteFile(buffer);
			}

		} while(FindNextFile(hp, &info)); 
		
		FindClose(hp);
	}
}

namespace {

#if GLF_GRAPHICAL_API_OPENGL_ES
#include <GLES/egl.h>

bool CreateGLESContext(const CreationSettings& cs, PlatformInfo& pi, int version) {

	EGLDisplay gLDisplay = 0;
	EGLConfig gLConfig = 0;
	EGLSurface gLSurface = 0;
	EGLContext gLContext = 0;
	
	gLDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
	
	if(gLDisplay) {
		EGLint majorVersion, minorVersion;

		if(eglInitialize(gLDisplay, &majorVersion, &minorVersion) == EGL_TRUE) {
			EGLint numConfigs;
			EGLint attribs[] = 
			{
				EGL_SURFACE_TYPE,		EGL_WINDOW_BIT,
				EGL_NATIVE_RENDERABLE,	EGL_FALSE,
				EGL_RENDERABLE_TYPE,	version  == 1 ? EGL_OPENGL_ES_BIT : EGL_OPENGL_ES2_BIT,
				EGL_DEPTH_SIZE,			cs.zBufferSize,
				EGL_RED_SIZE,			5,
				EGL_GREEN_SIZE,			6, 
				EGL_BLUE_SIZE,			5, 
				EGL_ALPHA_SIZE,			0, 
				EGL_NONE
			};
			eglChooseConfig(gLDisplay, attribs, &gLConfig, 1, &numConfigs);
			pi.mInfo.mEGL.eglConfigAttribs = attribs;
			
			if(numConfigs == 0) {
				eglTerminate(gLDisplay);
			} else {
				const EGLint surfaceAttribs[] = { EGL_NONE };
				gLSurface = eglCreateWindowSurface(gLDisplay, gLConfig, (NativeWindowType)pi.hWindow, surfaceAttribs);
				pi.mInfo.mEGL.eglSurfaceAttribs = surfaceAttribs;

				EGLint contextAttribs[] =
				{
					EGL_CONTEXT_CLIENT_VERSION, version,
					EGL_NONE
				};
				gLContext = eglCreateContext(gLDisplay, gLConfig, EGL_NO_CONTEXT, contextAttribs);
				pi.mInfo.mEGL.eglContextAttribs = contextAttribs;

				eglBindAPI(EGL_OPENGL_ES_API);
				
				if(eglMakeCurrent(gLDisplay, gLSurface, gLSurface, gLContext) == EGL_TRUE) {
					pi.mInfo.mEGL.eglDisplay = (void*)gLDisplay;
					pi.mInfo.mEGL.eglConfig = (void*)gLConfig;
					pi.mInfo.mEGL.eglWindowSurface = (void*)gLSurface;
					pi.mInfo.mEGL.eglContext = (void*)gLContext;

					eglSwapInterval(gLDisplay, cs.waitVSync ? 1 : 0);

					return true;
				}
			}
		}
	}
	return false;
}

bool CreateExtraContext(PlatformInfo& pi, int i) {

	EGLDisplay gLDisplay = (EGLDisplay)pi.mInfo.mEGL.eglDisplay;
	EGLConfig gLConfig = (EGLConfig)pi.mInfo.mEGL.eglConfig;
	EGLContext gLMainContext = (EGLContext)pi.mInfo.mEGL.eglContext;
	EGLContext gLContext = eglCreateContext(gLDisplay, gLConfig, gLMainContext, (const EGLint*)pi.mInfo.mEGL.eglContextAttribs);
	if(gLContext) {
		pi.mExtraContext[i] = (void*)gLContext;
		return true;
	}
	return false;
}
#endif

#if GLF_GRAPHICAL_API_OPENGL
	#ifdef _MSC_VER // We'll need to link on GL
		#pragma comment(lib, "OpenGL32.lib")
	#endif
#include <GL/gl.h>

#ifndef GLF_WINDOWS_DISABLE_SDL

bool CreateGLContext(const CreationSettings& cs, PlatformInfo& pi) {

	// Configure GL Context before creation
	if (cs.pixelSize==16) {
		SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 4 );
		SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 4 );
		SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 4 );
		SDL_GL_SetAttribute( SDL_GL_ALPHA_SIZE, 4 );
	} else {
		SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 8 );
		SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 8 );
		SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 8 );
		SDL_GL_SetAttribute( SDL_GL_ALPHA_SIZE, 8 );
	}

	// disable if needed
	if (cs.waitVSync == false)
		SDL_GL_SetAttribute( SDL_GL_SWAP_CONTROL, 0 );

	SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, cs.zBufferSize);
	SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );

	if(cs.useStencilbuffer) {
		SDL_GL_SetAttribute( SDL_GL_STENCIL_SIZE, 1);
	} else {
		SDL_GL_SetAttribute( SDL_GL_STENCIL_SIZE, 0);
	}

	if(cs.antiAliasSetting != AA_NONE)
	{
		SDL_GL_SetAttribute( SDL_GL_MULTISAMPLEBUFFERS, 1);
		switch(cs.antiAliasSetting)
		{
		case AA_2X:
			SDL_GL_SetAttribute( SDL_GL_MULTISAMPLESAMPLES, 2);
			break;
		case AA_4X:
		case AA_4XR:
			SDL_GL_SetAttribute( SDL_GL_MULTISAMPLESAMPLES, 4);
			break;
		case AA_8X:
			SDL_GL_SetAttribute( SDL_GL_MULTISAMPLESAMPLES, 8);
			break;
		case AA_16X:
			SDL_GL_SetAttribute( SDL_GL_MULTISAMPLESAMPLES, 16);
			break;
		default:
			SDL_GL_SetAttribute( SDL_GL_MULTISAMPLESAMPLES, 16);
			break;
		}
	}

	return true;
}

#endif

bool CreateExtraContext(PlatformInfo& pi, int i) {

	HDC hdc = (HDC)pi.mInfo.mWindows.hDc;
	HGLRC hrc = wglCreateContext(hdc);
	if(hrc)
	{
		wglShareLists((HGLRC)pi.mInfo.mWindows.hRc, hrc);
		pi.mExtraContext[i] = (void*)hrc;
		return true;
	}
	return false;
}
#endif

#if GLF_WINDOWS_EMULATE_EXTERNAL_DISPLAYS

HWND gSecondWindow = 0;

LONG WINAPI WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{ 
	if (hWnd != gSecondWindow)
		return DefWindowProc(hWnd, uMsg, wParam, lParam);
	switch (uMsg) 
	{
	case WM_CLOSE:
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
#if 1
	case WM_PAINT: 
		{
			HDC hdc;
			PAINTSTRUCT ps;
			hdc = BeginPaint(hWnd, &ps);
			RECT rect;
			GetClientRect(hWnd, &rect);
			FillRect(hdc, &rect, CreateSolidBrush(RGB(0, 0, 0)));
			EndPaint(hWnd, &ps);
		}
		return 0;	
#endif
	};
	return DefWindowProc(hWnd, uMsg, wParam, lParam); 
} 

HWND CreateGLCompatibleWindow(char* title, 
							  int x, int y, 
							  int width, int height, 
							  HDC hMainDC)
{
	static HINSTANCE hInstance = 0;
	// Only register the window class once
	if (!hInstance) {
		hInstance = GetModuleHandle(NULL);
		WNDCLASS wc;
		memset(&wc, 0, sizeof(WNDCLASS));
		wc.style			= CS_OWNDC;
		wc.lpfnWndProc		= (WNDPROC)WindowProc;
		wc.hInstance		= hInstance;
		wc.hIcon			= LoadIcon(NULL, IDI_WINLOGO);
		wc.hCursor			= LoadCursor(NULL, IDC_NO);
		wc.lpszClassName	= "WinGLClass";
		if (!RegisterClass(&wc)) {
			GLF_ASSERT(!"Register class failed");
			return 0;
		}
	}
	// Create the new window
	HWND hWnd = CreateWindowA(
		"WinGLClass", title, 
		WS_OVERLAPPED | WS_CAPTION,
		x, y, width, height, 
		0, 0, hInstance, 0);
	if (!hWnd) {
		GLF_ASSERT(!"Create opengl window failed");
		return 0;
	}
	// Copy main HDC pixel format
	HDC hDC = GetDC(hWnd);
	PIXELFORMATDESCRIPTOR  pfd;	
	const int pixelFormat = GetPixelFormat(hMainDC);
	DescribePixelFormat(hMainDC, pixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &pfd);
	SetPixelFormat(hDC, pixelFormat, &pfd);
	ReleaseDC(hWnd, hDC);
	return hWnd;
}    

#endif

}

App::Impl* gAppImpl = 0;

bool getWifiMacAddress(char* buffer, int length)
{
	IP_ADAPTER_INFO AdapterInfo[16];       
	DWORD dwBufLen = sizeof(AdapterInfo);  // Save memory size of buffer
	
	if(GetAdaptersInfo(AdapterInfo, &dwBufLen) != ERROR_SUCCESS)
		return false;
  
	PIP_ADAPTER_INFO pAdapterInfo = AdapterInfo;

	
	do {
		BYTE* addr = pAdapterInfo->Address;
		sprintf_s(buffer, length, "%02X:%02X:%02X:%02X:%02X:%02X", addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
		//assume first is the right one...
		break;
		//PrintMACaddress(pAdapterInfo->Address);
		pAdapterInfo = pAdapterInfo->Next;
	}
	while(pAdapterInfo);

	return true;
}

struct App::Impl : ImplAppBase {
#ifndef GLF_WINDOWS_DISABLE_SDL
	SDL_Surface* mSdlScreen;
#endif
	int mFlags;

	Impl(App& app, glf::App::CreationFlag cf) :
		ImplAppBase(app)
#ifndef GLF_WINDOWS_DISABLE_SDL
		, mSdlScreen(0)
#endif
		, mFlags(0)
		{

		gAppImpl = this;

		PropertyMap& pm = PropertyMap::Instance();

		char address[18];
		if(getWifiMacAddress(address, sizeof(address)))
		{
			pm.SetProperty("device.wifiMacAddress", address);
		}
		char buffer[256];
#if 0
		const char* launchCountFilename = "launchCount.txt";
		FILE* f = fopen(launchCountFilename, "r");
		int launchCount = 0;
		if(f) {
			fscanf(f, "%d", &launchCount);
			fclose(f);
		}
		
		
		pm.SetPropertyT("session.launchCount", launchCount, true);
		++launchCount;
		f = fopen(launchCountFilename, "w");
		if(f) {
			fprintf(f, "%d", launchCount);
			fclose(f);
		}
#endif
		{
			DWORD length = sizeof(buffer);
			GetComputerName(buffer, &length);
			pm.SetProperty("device.name",			buffer);
		}
		pm.SetProperty("device.systemName",		"windows");
		{
			OSVERSIONINFO osvi ={0};
			osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
			GetVersionEx(&osvi);
			sprintf(buffer, "%d.%d.%d", osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber);
			pm.SetProperty("device.systemVersion",	buffer);
			
			char buffer2[256];
			sprintf(buffer2, "pc-windows-%s", buffer);
			pm.SetProperty("device.machine",	buffer2);	//iPhone3,1
		}
		pm.SetProperty("device.model",			"PC");	//iPhone
		pm.SetProperty("device.localizedModel",	"PC");	//iPhone

		pm.SetProperty("hw.platform",	"win32");
	
		struct Locale {
			static void SetInfo(PropertyMap& pm, const char* key, int id) {
				char buffer[32];
				GetLocaleInfoA(LOCALE_USER_DEFAULT, id, buffer, sizeof(buffer));
				pm.SetProperty(key, buffer);
			}
		};
		
		Locale::SetInfo(pm, "locale.countryCode",		LOCALE_SISO3166CTRYNAME);
		Locale::SetInfo(pm, "locale.languageCode",		LOCALE_SISO639LANGNAME);
		Locale::SetInfo(pm, "locale.currencySymbol",	LOCALE_SCURRENCY);
		Locale::SetInfo(pm, "locale.groupingSeparator",	LOCALE_STHOUSAND);
		Locale::SetInfo(pm, "locale.decimalSeparator",	LOCALE_SDECIMAL);

		const char* cmd = GetCommandLine();

		const char* arg0 = GetGlobals()->argv[0];
		
		const char* lastBackSlash = Strrchr(arg0, '\\');

		char working[1024] = ".";

		if(lastBackSlash) {
			if(arg0[0] == '"') { //skip "
				arg0 += 1;
			}
			Sprintf_s(working, arg0, lastBackSlash - arg0);

			working[lastBackSlash - arg0] = '\0';
		}

		//replace \ with /
		char* buf = working;
		do {
			if(*buf == '\\') {
				*buf = '/';
			}
		} while(*buf++);

		
		if(!TestFlag(cf, CREATIONFLAG_WIN32_DONT_CHANGE_CWD)) {
			SetCurrentDirectory(working);
		}

		char currentDirectory[1024];
		GetCurrentDirectory(sizeof(currentDirectory), currentDirectory);

		mApp.mFs.SetDir(ios::loc_data, currentDirectory);

		mApp.mFs.SetDataDir(".");

		if(arg0 == NULL || strlen(arg0) < 4) {
			arg0 = "UnknownAppName.exe";
			lastBackSlash = NULL;
		}

		char exe[512];
		Strcpy(exe, lastBackSlash ? lastBackSlash + 1 : arg0);
		exe[strlen(exe) - 4] = '\0';

		char homeDir[1024];
		const char* APPDATA = std::getenv("APPDATA");
		if(!TestFlag(cf, CREATIONFLAG_WIN32_DONT_CHANGE_APPDATA) && APPDATA != NULL && strlen(APPDATA) > 0) {
			Sprintf_s(homeDir, "%s\\gameloft", APPDATA);
			CreateDirectory(homeDir, 0);
			Strcat_s(homeDir, "\\");
			Strcat_s(homeDir, exe);
		} else {
			JoinPath(homeDir, currentDirectory, "home");
		}
		mApp.mFs.SetDir(ios::loc_home, homeDir);
		
		char tempDir[1024];
		const char* TEMP = std::getenv("TEMP");
		if(!TestFlag(cf, CREATIONFLAG_WIN32_DONT_CHANGE_TMP) && TEMP != NULL && strlen(TEMP) > 0) {
			Sprintf_s(tempDir, "%s\\%s", TEMP, exe);
		} else {
			JoinPath(tempDir, currentDirectory, "tmp");
		}
		mApp.mFs.SetDir(ios::loc_tmp, tempDir);

		CreateDirectory(homeDir, 0);
		CreateDirectory(tempDir, 0);

		mApp.mFs.EmptyDir("", ios::loc_tmp);
	}

	bool Init(CreationSettings& cs) {

		bool success = true;

		mApp.mPlatformInfos.hWindow = 0;
		mApp.mPlatformInfos.hSecondWindow = 0;

#ifndef GLF_WINDOWS_DISABLE_SDL
		if(cs.createWindow) {

			SDL_Init(SDL_INIT_VIDEO);

			int csWindowPosX = cs.windowPosX;
			int csWindowPosY = cs.windowPosY;
			
			if(gArgPosX != CreationSettings::DEFAULT_VALUE)
				csWindowPosX = gArgPosX;
			if(gArgPosY != CreationSettings::DEFAULT_VALUE)
				csWindowPosY = gArgPosY;

			int posX	= csWindowPosX == CreationSettings::DEFAULT_VALUE ? CW_USEDEFAULT : csWindowPosX;
			int posY	= csWindowPosY == CreationSettings::DEFAULT_VALUE ? CW_USEDEFAULT : csWindowPosY;
			
			//command line takes priority over cs.deviceName
			if(gDeviceName[0] == 0 && cs.deviceName)
				Strcpy_s(gDeviceName, cs.deviceName);
			
			const DeviceInfos& di = GetDeviceInfo(gDeviceName);
			
			int width	= cs.windowWidth == CreationSettings::DEFAULT_VALUE ? di.screenWidth : cs.windowWidth;
			int height	= cs.windowHeight == CreationSettings::DEFAULT_VALUE ? di.screenHeight : cs.windowHeight;

			mFlags = SDL_HWSURFACE|SDL_RESIZABLE;

			if(cs.isFullScreen) {
				mFlags |= SDL_FULLSCREEN;
			}

			if(cs.hiddenFrame) {
				mFlags |= SDL_NOFRAME;
			}

#ifdef GLF_GRAPHICAL_API_OPENGL
			if(cs.driverType & OPENGL) {
				mFlags |= SDL_OPENGL;
			}
#endif
			
			SDL_WM_SetCaption(cs.appName, 0);

			PlatformInfo& pi= mApp.mPlatformInfos;

			//MoveWindow(wmInfo.window, posX, posY, width, height, true);


#ifdef GLF_GRAPHICAL_API_OPENGL
			if(cs.driverType & OPENGL){
				success = CreateGLContext(cs, pi);
			}else
#endif
			{
				// no API selected, pass...
			}

			if(cs.hiddenWindow) {
				mFlags |= SDL_HIDDENWND;
			}			
			
			glf::DisplayEvent displayConnectingEvent(glf::DET_CONNECTING);
			displayConnectingEvent.mResolutions[0].x = width;
			displayConnectingEvent.mResolutions[0].y = height;
			GetAppEventMgr().SendEvent(displayConnectingEvent);

			if(posX != CW_USEDEFAULT && posY != CW_USEDEFAULT)
			{
				char buffer[1024];
				sprintf(buffer, "SDL_VIDEO_WINDOW_POS=%d,%d", posX, posY);
				SDL_putenv(buffer);
			}
			mSdlScreen = SDL_SetVideoMode(width, height, 0, mFlags);

			glf::DisplayEvent displayConnectedEvent(glf::DET_CONNECTED);
			displayConnectedEvent.mResolutions[0].x = mSdlScreen->w;
			displayConnectedEvent.mResolutions[0].y = mSdlScreen->h;
			GetAppEventMgr().PostEvent(displayConnectedEvent);

			SDL_SysWMinfo wmInfo;
			SDL_VERSION(&wmInfo.version);
			SDL_GetWMInfo(&wmInfo);
			
			pi.hWindow		            = wmInfo.window;
			pi.mInfo.mWindows.hInstance	= GetModuleHandle(0);
			pi.mInfo.mWindows.hRc		= wmInfo.hglrc;
			pi.mInfo.mWindows.hDc		= GetDC(wmInfo.window);
#ifdef GLF_GRAPHICAL_API_OPENGL_ES
			if(cs.driverType & OPENGL_ES){
				if( cs.driverType & OPENGL_ES_2 && CreateGLESContext(cs, pi, 2) ){
					success = true;
				} else {
					if(cs.driverType & OPENGL_ES_1){
						success = CreateGLESContext(cs, pi, 1);
					}
				}
			}else 
#endif
#if defined(GLF_GRAPHICAL_API_OPENGL) || defined(GLF_GRAPHICAL_API_OPENGL_ES)
			for(int i = 0; i < cs.extraContexts;) {
				if(CreateExtraContext(pi, i)) {
					++i;
				} else {
					--cs.extraContexts;
				}
			}
#else
			cs.extraContexts = 0;
#endif
		}
#if defined(GLF_GRAPHICAL_API_OPENGL) || defined(GLF_GRAPHICAL_API_OPENGL_ES)
			PropertyMap& pm = PropertyMap::Instance();
			//pm.SetProperty("gfx.gl.vendor", reinterpret_cast<const char*>(glGetString(GL_VENDOR)));
			//pm.SetProperty("gfx.gl.version", reinterpret_cast<const char*>(glGetString(GL_VERSION)));
			//pm.SetProperty("gfx.gl.renderer", reinterpret_cast<const char*>(glGetString(GL_RENDERER)));
#endif
#endif

#if GLF_ENABLE_DEBUGGER
		if (cs.enableDebugger)
		{
			debugger::Debugger::GetInstance()->Init(cs.driverType);
		}
#endif

		return success;
	}

	bool Deinit()
	{
		return true;
	}

	void Update() {

#ifndef GLF_WINDOWS_DISABLE_SDL
		SDL_PumpEvents();
		SDL_Event evt;		
		//Pop all events except ones that are used by input devices
		while (SDL_PeepEvents(&evt, 1, SDL_GETEVENT, SDL_ALLEVENTS & ~(SDL_MOUSEEVENTMASK | SDL_KEYEVENTMASK)) == 1) {
			switch(evt.type) {
				case SDL_QUIT: {
					mApp.mIsRunning = false;
					if (mSdlScreen)
						GetAppEventMgr().PostEvent(glf::DisplayEvent(glf::DET_DISCONNECTED));
				} break;
				case SDL_VIDEORESIZE: {
					mSdlScreen->w = evt.resize.w;
					mSdlScreen->h = evt.resize.h;
					glf::DisplayEvent displayEvent(glf::DET_SCREEN_RESIZED);
					displayEvent.mResolutions[0].x = mSdlScreen->w;
					displayEvent.mResolutions[0].y = mSdlScreen->h;
					GetAppEventMgr().PostEvent(displayEvent);
				} break;
				case SDL_ACTIVEEVENT: {
					//if(evt.active.state & SDL_APPACTIVE)
					//{
					//	if(evt.active.gain)
					//	{
					//		GetAppEventMgr().SendEvent(glf::SystemEvent(glf::SET_ENTERFOREGROUND));
					//		GetAppEventMgr().SendEvent(glf::SystemEvent(glf::SET_BECAME_ACTIVE));
					//	}
					//	else
					//	{
					//		GetAppEventMgr().SendEvent(glf::SystemEvent(glf::SET_WILL_RESIGN_ACTIVE));
					//		GetAppEventMgr().SendEvent(glf::SystemEvent(glf::SET_ENTERBACKGROUND));
					//		
					//	}
					//}
				} break;
			}
		}
#endif

#if GLF_ENABLE_DEBUGGER
		if(mApp.mCreationSettings.enableDebugger &&
			mApp.GetInputMgr().GetKeyboard(0).WasPressed(Keyboard::KEY_F1) &&
			!mApp.GetInputMgr().GetKeyboard(0).IsDown(Keyboard::KEY_LCTRL) &&
			!mApp.GetInputMgr().GetKeyboard(0).IsDown(Keyboard::KEY_LSHIFT))
		{
			debugger::SpawnRemoteDebugger();
		}
		
		debugger::Debugger::GetInstance()->UpdateFrame();
#endif

#if GLF_WINDOWS_EMULATE_EXTERNAL_DISPLAYS
		
		if(mApp.GetInputMgr().GetKeyboard(0).WasPressed(Keyboard::KEY_F2) &&
			!mApp.GetInputMgr().GetKeyboard(0).IsDown(Keyboard::KEY_LCTRL) &&
			!mApp.GetInputMgr().GetKeyboard(0).IsDown(Keyboard::KEY_LSHIFT))
		{
			if (!mApp.mPlatformInfos.hSecondWindow)
				CreateSecondWindow();
			else
				DestroySecondWindow();
		}

#endif

		SleepEx(0, true);
	}
	
	void GetWindowSize(int& width, int& height, int windowIndex) const {
		
		switch(windowIndex)
		{
		case 0:	
			if (mApp.mPlatformInfos.hWindow)
			{
				RECT rect;
				GetClientRect((HWND)mApp.mPlatformInfos.hWindow, &rect);
				width = rect.right - rect.left;
				height = rect.bottom - rect.top;
				return;
			}
			break;
		case 1:
			if (mApp.mPlatformInfos.hSecondWindow)
			{
				RECT rect;
				GetClientRect((HWND)mApp.mPlatformInfos.hSecondWindow, &rect);
				width = rect.right - rect.left;
				height = rect.bottom - rect.top;
				return;
			}
			break;
		}
		width = height = 0;
	}

	void *GetFrameBufferObject(int windowIndex)
	{
		switch(windowIndex)
		{
		case 0:			
			return (void *)(mApp.mPlatformInfos.hWindow != 0 ? 0 : -1);
		case 1:
			return (void *)(mApp.mPlatformInfos.hSecondWindow != 0 ? 0 : -1);
		}
		return (void *)-1;
	}
	
	bool HasFrameBufferObject(int windowIndex) const	
	{
		switch(windowIndex)
		{
		case 0:			
			return mApp.mPlatformInfos.hWindow != 0;
		case 1:
			return mApp.mPlatformInfos.hSecondWindow != 0;
		}
		return false;
	}

#ifdef GLF_GRAPHICAL_API_OPENGL

	bool Prepare(int windowIndex) {
		switch(windowIndex)
		{
		case 0:
			if (mApp.mPlatformInfos.hWindow) {
				wglMakeCurrent(
					(HDC)mApp.mPlatformInfos.mInfo.mWindows.hDc, 
					(HGLRC)mApp.mPlatformInfos.mInfo.mWindows.hRc);
				return true;
			}
		case 1:
			if (mApp.mPlatformInfos.hSecondWindow) {
				wglMakeCurrent(
					(HDC)mApp.mPlatformInfos.mInfo.mWindows.hSecondDc, 
					(HGLRC)mApp.mPlatformInfos.mInfo.mWindows.hRc);
				return true;
			}
			break;
		}
		return false;
	}

#endif

	DisplayStatus GetDisplayStatus(int windowIndex)
	{
		switch(windowIndex)
		{
		case 0:
			return DS_CONNECTED;
		case 1:
			return mApp.mPlatformInfos.hSecondWindow ? DS_CONNECTED : DS_AVAILABLE;
		}
		return DS_UNAVAILABLE;
	}

	void Present(int windowIndex) {
		switch(windowIndex)
		{
		case 0:
#ifdef GLF_GRAPHICAL_API_OPENGL_ES
			if (mApp.mCreationSettings.swapBuffersOnPresent)
				eglSwapBuffers(
					(EGLDisplay)mApp.mPlatformInfos.mInfo.mEGL.eglDisplay, 
					(EGLSurface)mApp.mPlatformInfos.mInfo.mEGL.eglWindowSurface);
#elif !defined(GLF_WINDOWS_DISABLE_SDL)
			if (mApp.mCreationSettings.swapBuffersOnPresent)
				SDL_GL_SwapBuffers();
#else 
			if (mApp.mCreationSettings.swapBuffersOnPresent)
				SwapBuffers((HDC)mApp.mPlatformInfos.mInfo.mWindows.hDc);
#endif
			break;
		case 1:
			if (mApp.mCreationSettings.swapBuffersOnPresent)
				SwapBuffers((HDC)mApp.mPlatformInfos.mInfo.mWindows.hSecondDc);
			break;
		};
	}

#if GLF_WINDOWS_EMULATE_EXTERNAL_DISPLAYS

	bool ConnectDisplay(int windowIndex)
	{
		if (windowIndex == 1 && GetDisplayStatus(windowIndex) == DS_AVAILABLE) {			
			CreateSecondWindow();
			return true;
		}
		return false;
	}

	bool DisconnectDisplay(int windowIndex)
	{
		if (windowIndex == 1 && GetDisplayStatus(windowIndex) == DS_CONNECTED) {
			DestroySecondWindow();
			return true;
		}
		return false;
	}	

#endif

	void Run() {
	}

	void* GetMainContext() const {
#ifdef GLF_GRAPHICAL_API_OPENGL_ES
		return mApp.mPlatformInfos.mInfo.mEGL.eglContext;
#elif defined(GLF_GRAPHICAL_API_OPENGL)
		return mApp.mPlatformInfos.mInfo.mWindows.hRc;
#else
		return (void*)1;
#endif
	}

	void* GetCurrentContext() const {
#ifdef GLF_GRAPHICAL_API_OPENGL_ES
		return (void*)eglGetCurrentContext();
#elif defined(GLF_GRAPHICAL_API_OPENGL)
		return (void*)wglGetCurrentContext();
#else
		return (void*)(Thread::sIsMain() ? 1 : 0);
#endif
	}

	bool SetCurrentContext(void* ctx) {
#ifdef GLF_GRAPHICAL_API_OPENGL_ES
		PlatformInfo& pi = mApp.mPlatformInfos;
		if(ctx) {
			EGLSurface surface = (EGLSurface)pi.mInfo.mEGL.eglWindowSurface;
			return eglMakeCurrent((EGLDisplay)pi.mInfo.mEGL.eglDisplay, surface, surface, (EGLContext)ctx) == EGL_TRUE;
		}
		return eglMakeCurrent((EGLDisplay)pi.mInfo.mEGL.eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT) == EGL_TRUE;
#elif defined(GLF_GRAPHICAL_API_OPENGL)
		return wglMakeCurrent((HDC)mApp.mPlatformInfos.mInfo.mWindows.hDc, (HGLRC)ctx) != 0;
#else
		return false;
#endif
	}

	const char* GetDeviceName() const {
		return GetDeviceInfo(gDeviceName).name;
	}

#if GLF_WINDOWS_EMULATE_EXTERNAL_DISPLAYS

	void ResizeWindow(HWND hWnd, int w, int h) {
		RECT clientRect, windowRect;		
		GetClientRect(hWnd, &clientRect);
		GetWindowRect(hWnd, &windowRect);
		POINT offset;
		offset.x = (windowRect.right - windowRect.left) - clientRect.right;
		offset.y = (windowRect.bottom - windowRect.top) - clientRect.bottom;
		// Resize the window a bit more in order to get the required size for the client rect
		MoveWindow(hWnd, windowRect.left, windowRect.top, w + offset.x, h + offset.y, FALSE);
	}

	void CreateSecondWindow() {
		if (mApp.mPlatformInfos.hSecondWindow)
			return;
		//Choose the smallest screen size as default
		glf::DisplayEvent dspConnecting(glf::DET_CONNECTING);
		dspConnecting.mCurrentResolution = 0;
		dspConnecting.mRank = 1;		
		dspConnecting.mResolutionCount = 0;		
		dspConnecting.mResolutions[dspConnecting.mResolutionCount++] = Point<ushort>(480, 320); //iPhone		
		dspConnecting.mResolutions[dspConnecting.mResolutionCount++] = Point<ushort>(960, 640); //iPhone4
		dspConnecting.mResolutions[dspConnecting.mResolutionCount++] = Point<ushort>(1024, 768); //iPad
		dspConnecting.mResolutions[dspConnecting.mResolutionCount++] = Point<ushort>(640, 480); //VGA
		dspConnecting.mResolutions[dspConnecting.mResolutionCount++] = Point<ushort>(800, 600); //SVGA
		dspConnecting.mResolutions[dspConnecting.mResolutionCount++] = Point<ushort>(1280, 720); //HD 720p
		dspConnecting.mResolutions[dspConnecting.mResolutionCount++] = Point<ushort>(1920, 1080); //HD 1080p
		// Send connected display event
		if (!GetAppEventMgr().SendEvent(dspConnecting) || dspConnecting.mCurrentResolution >= dspConnecting.mResolutionCount)
		{
			glf::Console::Println("User abort usage of second display");
			return;
		}
		// Create compatible OpenGL window
		char title[256];
		sprintf(title, "External Display %dx%d", 
			dspConnecting.mResolutions[dspConnecting.mCurrentResolution].x, 
			dspConnecting.mResolutions[dspConnecting.mCurrentResolution].y);
		RECT rect;
		GetWindowRect((HWND)mApp.mPlatformInfos.hWindow, &rect);
		mApp.mPlatformInfos.hSecondWindow = CreateGLCompatibleWindow(
			title, rect.right, rect.top,
			dspConnecting.mResolutions[dspConnecting.mCurrentResolution].x, 
			dspConnecting.mResolutions[dspConnecting.mCurrentResolution].y, 
			(HDC)mApp.mPlatformInfos.mInfo.mWindows.hDc);
		gSecondWindow = (HWND)mApp.mPlatformInfos.hSecondWindow;
		if (!mApp.mPlatformInfos.hSecondWindow)
			return;
		ResizeWindow((HWND)mApp.mPlatformInfos.hSecondWindow, 
			dspConnecting.mResolutions[dspConnecting.mCurrentResolution].x, 
			dspConnecting.mResolutions[dspConnecting.mCurrentResolution].y);
		mApp.mPlatformInfos.mInfo.mWindows.hSecondDc = GetDC((HWND)mApp.mPlatformInfos.hSecondWindow);		
		ShowWindow((HWND)mApp.mPlatformInfos.hSecondWindow, SW_SHOWNA);
		// Post connected display event after window creation
		glf::DisplayEvent dspConnected(glf::DET_CONNECTED);
		dspConnected.mRank = 1;
		int width, height;
		GetWindowSize(width, height, 1);
		dspConnected.mResolutions[0].x = width;
		dspConnected.mResolutions[0].y = height;
		GetAppEventMgr().PostEvent(dspConnected);
	}

	void DestroySecondWindow() {
		if (!mApp.mPlatformInfos.hSecondWindow)
			return;
		// Send disconnected display event
		glf::DisplayEvent dspEvent(glf::DET_DISCONNECTING);
		dspEvent.mRank = 1;		
		GetAppEventMgr().SendEvent(dspEvent);
		// Free ressources
		wglMakeCurrent(NULL, NULL);
		ReleaseDC((HWND)mApp.mPlatformInfos.hSecondWindow, (HDC)mApp.mPlatformInfos.mInfo.mWindows.hSecondDc);
		DestroyWindow((HWND)mApp.mPlatformInfos.hSecondWindow);
		mApp.mPlatformInfos.hSecondWindow = 0;
		// Restore context on main window
		Prepare(0);
		// Post disconnected display event after window destruction
		glf::DisplayEvent dspDisconnected(glf::DET_DISCONNECTED);
		dspDisconnected.mRank = 1;
		GetAppEventMgr().PostEvent(dspDisconnected);
	}

#endif

	void StartRemoteController(const std::string& ip, int port) {
		if(mApp.mRemoteController)
			delete mApp.mRemoteController;
		AddrIp4 addr;
		if (!ip.empty())
			addr = Socket::MakeAddr(ip.c_str(), port); 
		else
			addr = Socket::MakeAddr("127.0.0.1", port); 
		mApp.mRemoteController = new remote::Controller(addr);
	}

};

bool App::IsRunnable() const {
	return true;
}

glf::Globals* gGlobals = 0;

Globals* GetGlobals() {
	return gGlobals;
}

void SetGlobals(Globals* pGlobals)
{
	gGlobals = pGlobals;
}

// This was done to ease transition code when glf usage for glitch became mandatory.
// If you want to refactor this code, please test it with the Glitch Editor.
void init()
{
	if (gGlobals == NULL) 
	{
		gGlobals = new Globals();
	}
}

int Main(int argc, char** argv) {
	setvbuf(stdout, NULL, _IONBF, 1024);
	init();
	
	gGlobals->argc = argc;
	gGlobals->argv = argv;

	App* win32App = NewApp(argc, argv);
	enum ArgSwitch {
		ARG_UNKNOWN,
		ARG_POSX,
		ARG_POSY,
	};
	ArgSwitch argSwitch = ARG_UNKNOWN;

	const char* envDevice = getenv("DEVICE");
	if(envDevice != NULL) {
		Strcpy_s(gDeviceName, envDevice);
	}

	//use optparse or something...
	for(int i = 0; i < argc; ++i) {
		const char* arg = argv[i];
		
		if(arg[0] == '-') {
			++arg;
			argSwitch = ARG_UNKNOWN;
			if(Stricmp(arg, "posx") == 0) {
				argSwitch = ARG_POSX;
			} else if(Stricmp(arg, "posy") == 0) {
				argSwitch = ARG_POSY;
			}

			for(size_t j = 0; j < GLF_ASIZEOF(gDeviceInfos); ++j) {
				const DeviceInfos& di = gDeviceInfos[j];
				if(Stricmp(arg, di.name) == 0) {
					Strcpy_s(gDeviceName, di.name);
					break;
				}
			}
		}
		else
		{
			switch(argSwitch) {
			case ARG_POSX:
				int x;
				if(sscanf(arg, "%d", &x) == 1) {
					gArgPosX = x;
				}
				break;
			case ARG_POSY:
				int y;
				if(sscanf(arg, "%d", &y) == 1) {
					gArgPosY = y;
				}
				break;
			}
		}
	}

	if(win32App && win32App->MyInit()) {
		win32App->Run();
		win32App->Destroy();
	}
	
	delete gGlobals;

	return 0;
}

} //namespace glf

#if !GLF_COMPILE_AS_LIB

int main(int argc, char** argv) {
	return glf::Main(argc, argv);
}

#ifdef GLF_AS_DLL
int initDLLMain(int argc, char** argv) {
	return main(argc, argv);
}
#endif

//App* win32App = NewApp(argc, argv){
//	return 0;
//}

#endif